<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        /* 创建对象的方式:
            -字面量创建
            -内置构造函数创建
            -工厂模式创建
            -自定义构造函数创建
            -Object.create()
        */

        //字面量
        var obj = {
            name: "小明",
            age: 20
        }

        //内置构造函数
        var obj1 = new Object();
        obj1.name = "大明"
        console.log(obj1); //{name: '大明'}

        //工厂函数
        function Factory(name, age) {
            var obj2 = {};
            obj2.name = name;
            obj2.age = age;
            return obj2;
        }
        var obj2 = Factory("张三", 20);
        console.log(obj2); //{name: '张三', age: 20}

        //自定义构造函数
        function Person(name, age) {
            this.name = name;
            this.age = age;
            return {
                name: "钟鸣"
            }
        }
        //构造函数原型上添加方法
        Person.prototype.fn = function () {
            console.log("fn...");
        }
        var p1 = new Person("张三", 40)
        //有返回值 自身有name  值为返回的值
        console.log(p1); //{name: '钟鸣'}

        /* 
        new做了什么事?
            1.创建一个空的简单 JavaScript 对象 (即{});
            2.新创建的对象添加属性__proto__,将该属性链接至构造函数的原型对象;
            3.新创建的对象作为this的上下文
            4.如果该函数没有返回值,则返回this
        */

        function Person(name, age) {
            this.name = name;
            this.age = age;
            return {
                name: "123"
            }
        }

        Person.prototype.fn = function () {
            console.log("fn...");
        }
        // 手写new
        function myNew(constr, ...argv) {
            //创建空对象
            var obj = {};
            //调用构造函数并改变this指向  
            //re 接收的是构造函数的返回值
            const re = constr.call(obj, ...argv);
            //改变新创建对象的原型链
            obj.__proto__ = constr.prototype;

            //根据构造函数调用后的返回值类型,确定new的返回值是实例化对象,还是构造函数返回的对象
            /* 构造函数调用返回基本数据类型,则不影响实例化调用返回的实例化对象 
            构造函数调用返回对象数据类型,则构造函数实例化后不再返回实例化对象,而是返回return的对象*/
            if (Object.prototype.toString.call(re) === "[object Object]" || Object.prototype.toString.call(re) ===
                "[object Function]") {
                return re;
            }
            return obj;

        }
        const p2 = myNew(Person, "老六", 18);
        console.log(p2);
    </script>
</body>

</html>